Open-Close Principle (OCP)

Descripcion

El principio Open-Close nos dice que un modulo debe de ser abierto (open) para su expansión y cerrado (close) para su modificación.

Esto quiere decir que si queremos añadir nuevas funcionalidades solo deberíamos de escribir nuevas clases y no modificar las existentes.

Ejemplo código
Metodo

Para mostrar este principio un ejemplo clásico es una aplicación que tiene que trabajar con ciertas formas geometricas.

Tendremos una clase forma, dos clases que heredan de esta, cuadrado y circulo y la clase principal, que se encarga de gestionar todas las demás clases

El objetivo de esta aplicación es que si queremos introducir una nueva forma, un triangulo por ejemplo, para cumplir con el OCP deberíamos de escribir solo el código de la nueva clase triangulo y cambiar la clase principal para hacer uso de esta, no deberíamos de tener que modificar la clase forma ya que esta debería de ser cerrada a modificación pero abierta a extension.

El siguiente código muestra un ejemplo de la aplicación que NO cumpliría con el OCP:

Clase principal:

import java.util.ArrayList;

public class OpenClose {
    public static void main(String[] args) {
        Circulo primerCirculo = new Circulo();
        Cuadrado primerCuadrado = new Cuadrado();

        primerCirculo.radio = 33;
        primerCuadrado.lado = 44;

        ArrayList<Forma> lista = new ArrayList<Forma>();

        lista.add(primerCirculo);
        lista.add(primerCuadrado);

        Forma.imprimirAreaFormas(lista);
    }
}

Clase Forma:

import java.util.ArrayList;

public class Forma {
    public enum Tipo {CIRCULO, CUADRADO};

    public Tipo esTipo;

    public static void imprimirAreaFormas(ArrayList<Forma> lista){

        for (Forma forma : lista) {
            switch (forma.esTipo) {
                case CUADRADO:
                    Cuadrado.printArea((Cuadrado)forma);;
                    break;

                case CIRCULO:
                    Circulo.printArea((Circulo)forma);;
                    break;
                default:
                    break;
            }

        }
    }
}

Clase Cuadrado:

public class Cuadrado extends Forma {
    public int lado;

    Cuadrado(){
        esTipo = Tipo.CUADRADO;
    }

    public static void printArea(Cuadrado cuadrado){
        double areaCuadrado = cuadrado.lado * cuadrado.lado;
        System.out.println("El area del cuadrado es: " + areaCuadrado);
    }

}

Clase Circulo:

public class Circulo extends Forma {
    public int radio;

    Circulo(){
        esTipo = Tipo.CIRCULO;
    }

    public static void printArea(Circulo circulo){
        double areaCirculo = 3.14 * (circulo.radio * circulo.radio);
        System.out.println("El area del circulo es: " + areaCirculo);
    }
}

En el ejemplo anterior si queremos añadir una nueva forma (triangulo por ejemplo), tendríamos que modificar la estructura switch que tenemos en la clase forma para añadir la nueva forma, incluso si tuviesemos mas funciones a parte de printArea esa estructura switch estaría repetida varias veces y cada vez que añadimos una nueva forma tendríamos que modificar todas esas estructuras switch que tuviesemos.

Cumplir el OCP

El siguiente ejemplo cumple con el OCP, para ello utiliza funciones abstractas en la clase forma de esta manera aunque añadamos nuevas formas la clase forma permanece intacta sin necesidad de realizar modificaciones.

Clase principal

import java.util.ArrayList;

public class OpenClose {
    public static void main(String[] args) {
        Circulo primerCirculo = new Circulo();
        Cuadrado primerCuadrado = new Cuadrado();

        primerCirculo.radio = 33;
        primerCuadrado.lado = 44;

        ArrayList<Forma> lista = new ArrayList<Forma>();

        lista.add(primerCirculo);
        lista.add(primerCuadrado);

        Forma.imprimirAreaFormas(lista);
    }
}

Clase Forma

import java.util.ArrayList;

public abstract class Forma {

    protected abstract void printArea();

    public static void imprimirAreaFormas(ArrayList<Forma> lista){

        for (Forma forma : lista) {
            forma.printArea();
        }
    }
}

Clase Cuadrado

public class Cuadrado extends Forma {
    public int lado;

    protected void printArea(){
        int areaCuadrado = this.lado * this.lado;
        System.out.println("El area del cuadrado es: " + areaCuadrado);
    }

}

Clase Circulo

public class Circulo extends Forma {
    public int radio;

    protected void printArea(){
        double areaCirculo = 3.14 * (this.radio * this.radio);
        System.out.println("El area del circulo es: " + areaCirculo);
    }
}

En este nuevo ejemplo printArea es un metodo abstracto, que tenemos que implementar dentro de cada nueva forma, de manera que si ahora necesitamos crear una nueva forma como triangulo, solo tendríamos que crear la nueva clase triangulo e implementar ahí dentro la función, en la clase Forma no tenemos que realizar ninguna modificación ya que el método está abstraido, si tuviesemos mas métodos a parte de printArea solo tendríamos que implementarlos en las nuevas formas que vayamos añadiendo, no tendríamos el problema de la estrutura switch repetida varias veces a lo largo del código.

Tags

SOLID | Open-Close